﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Assets.Scripts.Data;
using Test30.Util;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;
using AnimatorController = UnityEditor.Animations.AnimatorController;


public class BuildPrefabs:Editor
{
    [MenuItem("Tools/BuildEntityPrefabs")]
    private static void BuildEntityPrefabs()
    {
        string path = Application.dataPath + Configs.PathConact + Configs.ResourcePath + Configs.EntitiesSpritesPath;
        LoadSprites(path);
        var groupes = SpriteList.GroupBy(o => o.name.RemoveSuffix(2));

        foreach (IGrouping<string, Sprite> groupe in groupes)
        {
            BuildEntityPrefab(groupe);
        }
    }
    private static void BuildEntityPrefab(IGrouping<string, Sprite> groupe)
    {
        var sprites = groupe.ToList();
        BuildClips(groupe.Key, sprites, Configs.AnimationFsm);
        var controller = BuildController(ClipList, groupe.Key);
        BuildPrefab(groupe.Key, sprites[0], controller);
    }
    private static void BuildPrefab(string name,Sprite sprite,AnimatorController controller)
    {
        GameObject go = new GameObject {name = name};
        //渲染组件
        SpriteRenderer spriteRenderer = go.AddComponent<SpriteRenderer>();
        spriteRenderer.sprite = sprite;
        //动画控制器组件
        Animator animator = go.AddComponent<Animator>();
        animator.runtimeAnimatorController = controller;
        PrefabUtility.CreatePrefab(
            Configs.AssetsPath + Configs.ResourcePath + Configs.EntityPrefabsPath + Configs.PathConact + go.name +
            Configs.PrefabFormat, go);
        DestroyImmediate(go);
        AssetDatabase.SaveAssets();
    }
    private static void LoadSprites(string path)
    {
        DirectoryInfo info = new DirectoryInfo(path);
        FileInfo[] images = info.GetFiles(Configs.Wildcard + Configs.ImageFormat);
        SpriteList.Clear();
        foreach (FileInfo image in images)
        {
            Sprite[] sp =
                Resources.LoadAll<Sprite>(Configs.EntitiesSpritesPath + Configs.PathConact +
                                          image.Name.RemoveSuffix(Configs.ImageFormat.Length));
            SpriteList.AddRange(sp);
        }

        CheckName(SpriteList.Select(o => o.name).ToList());
    }
    private static void CheckName(IEnumerable<string> list)
    {
        bool sameKeys = list.GroupBy(x => x).Any(c => c.Count() > 1);
        if (sameKeys)
            throw new ArgumentOutOfRangeException($"Entities美术资源有重名！");
    }
    private static void BuildClips(string name, List<Sprite> sprites, Dictionary<string,int> fsms)
    {
        ClipList.Clear();
        foreach (KeyValuePair<string, int> fsm in fsms)
        {
            var clip = SetClip(name, sprites, fsm.Key, fsm.Value);
            ClipList.Add(clip);
        }
    }
    private static AnimationClip SetClip(string name, List<Sprite> sprites, string fsm, int frameNums)
    {
        AnimationClip clip = new AnimationClip {frameRate = Configs.FrameRate};

        EditorCurveBinding curveBinding = new EditorCurveBinding
        {
            type = typeof(SpriteRenderer),
            path = "",
            propertyName = "m_Sprite"
        };
        ObjectReferenceKeyframe[] keyFrames = new ObjectReferenceKeyframe[frameNums + 1];

        for (int i = 0; i <= frameNums; i++)
        {
            var s = i == frameNums ? sprites[0] : sprites[i];

            keyFrames[i] = new ObjectReferenceKeyframe
            {
                time = i * Configs.AnimationInterval / (float)Configs.FrameRate,
                value = s,
            };
        }
        if (fsm == Configs.Fsm.Idle.ToString())
        {
            var settings = AnimationUtility.GetAnimationClipSettings(clip);
            settings.loopTime = true;
            AnimationUtility.SetAnimationClipSettings(clip, settings);
        }
        AnimationUtility.SetObjectReferenceCurve(clip, curveBinding, keyFrames);
        AssetDatabase.CreateAsset(clip,
            Configs.AssetsPath + Configs.ResourcePath + Configs.ClipsPath + name + fsm + Configs.ClipFormat);
        AssetDatabase.SaveAssets();
        return clip;
    }
    private static AnimatorController BuildController(List<AnimationClip> clips,string name)
    {
        string idle   = Configs.Fsm.Idle.ToString();
        string attack = Configs.Fsm.Attack.ToString();
        string hit    = Configs.Fsm.Hit.ToString();
        string dead   = Configs.Fsm.Dead.ToString();

        if (clips.Count < 1)
            throw new ArgumentNullException($"Clip片段为空");
        AnimatorController controller = AnimatorController.CreateAnimatorControllerAtPath(
            Configs.AssetsPath + Configs.ResourcePath + Configs.ControllersPath + name + Configs.ControllerFormat);

        // Add StateMachines
        AnimatorStateMachine sm = controller.layers[0].stateMachine;
        //add parameters
        controller.AddParameter(idle, AnimatorControllerParameterType.Trigger);
        controller.AddParameter(attack, AnimatorControllerParameterType.Trigger);
        controller.AddParameter(hit, AnimatorControllerParameterType.Trigger);
        controller.AddParameter(dead, AnimatorControllerParameterType.Trigger);

        //add States
        States.Clear();
        foreach (AnimationClip clip in clips)
        {
            var str = clip.name.RemovePrefix(name);
            States.Add(str,sm.AddState(str));
            States[str].motion = clip;
            if (clip.name == Configs.Fsm.Idle.ToString())
                sm.defaultState = States[str];
        }
        // Add Transitions
        var idleTransition = sm.AddAnyStateTransition(States[idle]);
        var attackTransition = sm.AddAnyStateTransition(States[attack]);
        var hitTransition = sm.AddAnyStateTransition(States[hit]);
        var deadTransition = sm.AddAnyStateTransition(States[dead]);

        idleTransition.AddCondition(AnimatorConditionMode.If,0,idle);
        idleTransition.duration = 0;

        attackTransition.AddCondition(AnimatorConditionMode.If, 0, attack);
        attackTransition.duration = 0;

        hitTransition.AddCondition(AnimatorConditionMode.If, 0, hit);
        hitTransition.duration = 0;

        deadTransition.AddCondition(AnimatorConditionMode.If, 0, dead);
        deadTransition.duration = 0;

        AssetDatabase.SaveAssets();
        return controller;
    }

    private static readonly Dictionary<string, AnimatorState> States = new Dictionary<string, AnimatorState>();
    private static readonly List<AnimationClip> ClipList = new List<AnimationClip>();
    private static readonly List<Sprite> SpriteList = new List<Sprite>();
}

